#include<iostream>
#include<functional>
#include<string>
#include<vector>
#include<stdint.h>
#include<unistd.h>
#include<sys/timerfd.h>
#include<memory>
#include<unordered_map>

//关于bind；
//bind 是一个函数模版，用来调整参数个数与参数顺序
// template <class Fn, class... Args> 
///* unspecified */ bind (Fn&& fn, Args&&... args);
//arglist 当中会使用到占位符 _1 _2等，这些占位符会被放到 placeholders 的命名空间当中，想要直接使用这些占位符可以直接展开
// using std::placeholders::_1;
// using std::placeholders::_2;
// using std::placeholders::_3;
//实践当中还是指定命名空间使用
//将bind 接口当做通用的函数适配器（修改可调用对象的接口，例如绑定参数、改变参数顺序）
// void Print(std::string s)
// {
//     std::cout << s << std::endl;
// }

void Print(const std::string &s , int num)
{
    std::cout << s <<  " " << num <<  std::endl;
}

// int main()
// {
//     auto func = bind(Print , "hello world" , std::placeholders::_1);
//     func(10);
//     return 0;
// }
//在项目当中可以利用bind， 将任务与处理方法进行绑定Push 进任务队列当中；这样处理就可以无需考虑有哪些任务处理方式了，均可以使用统一的方式进行调用

// int main()
// {
//     using Task = std::function<void()>;//创建任务
//     //利用数组充当任务队列
//     std::vector<Task> array;
//     //将任务push 进任务队列当中
//     array.push_back(std::bind(Print,"hello world", 100));
//     array.push_back(std::bind(Print,"hello", 200));
//     array.push_back(std::bind(Print,"world", 300));
//     array.push_back(std::bind(Print,"qinghua", 400));

//     //遍历任务队列执行任务
//     for(auto & f: array)
//     {
//         f();
//     }
//     return 0;
// }

//在高并发服务器当中，连接的超时处理：利用定时器；Linux 当中提供了两个函数：timerfd_create timerfd_settime,一个用来创建定时器，一个用来设定时间
// int timerfd_create(int clockid, int flags);
//第一个参数填CLOCK_MONOTONIC 表示以系统启动的事件进行递增的一个基准值，不会受系统时间改变的影响；第二个参数填0表示阻塞操作；
//timerfd_create 的返回值为文件描述符，定时器的操作与文件相关，当定时器超时的时候，系统会向这个定时器所对应的文件描述符当中写入一个8byte 的数据
// int timerfd_settime(int fd, int flags,const struct itimerspec *new_value,struct itimerspec *_Nullable old_value);
//第一个参数为文件描述符，即timerfd_create 的返回值，第二个参数填0表示相对时间，填1表示绝对时间，此处填0便可；第三个参数为新的超时时间，第四个参数为旧的超时时间，不适用设置为bulptr 就可以了
// 关于结构体struct itimespec:
// struct itimerspec
// {
//     struct timespec it_interval;//第一次超时之后隔多少秒再通知 interval:间隔
//     struct timespec it_value;//第一次超时时间
// };
// struct timespec
// {
//     __time_t tv_sec;		/* Seconds.  */  //秒
//     __syscall_slong_t tv_nsec;	/* Nanoseconds.  */  //ns
// };
// int main()
// {
//     //创建定时器，timerfd_create
//     int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);//阻塞
//     if(timerfd < 0)
//     {
//         perror("timerfd_create error");
//         return -1;
//     }

//     //设置超时时间，创建struct itimerspec 对象，并调用timerfd_settime
//     struct itimerspec itime;
//     itime.it_value.tv_sec = 1;
//     itime.it_value.tv_nsec = 0;
//     itime.it_interval.tv_sec = 1;
//     itime.it_interval.tv_nsec = 0;
//     timerfd_settime(timerfd , 0 , &itime , nullptr);
    
//     //从超时文件中以8byte中利用read 读取超时了多少次
//     while(1)
//     {
//         // sleep(8);
//         //此处的读取相当于是一种处理，如果在这其超时期间没有读取，文件中记录的就是在这段时间内超时的次数；倘若每秒都读取了，所以打印出来都是1
//         uint64_t times;
//         int n = read(timerfd ,&times, 8);
//         if(n < 0)
//         {
//             perror("read error");
//             return -1;
//         }
//         printf("超时了，这是第 %ld 次超时\n" , times);
//     }
//     //关闭文件描述符
//     close(timerfd);
//     return 0;
// }

//启动定时器去遍历所有的连接检查该链接的活跃情况，但如果连接数非常多那么遍历的效率就会很低：解决方法一：将连接以活跃时间为key值放入小根堆当中
//当遍历到无需释放的连接就停止遍历，因为往后的连接一定没有超时；还有一种处理方法：时间轮思想
//判定一个链接是否为非活跃连接，30s内没有通信则就将其判定为非活跃连接；但倘若在这30s内这个链接有通信，就需要对这个销毁连接的任务进行延时
//而作为一个时间轮并不关心任务类型，只要这个任务的时间到了就应该去执行 --> 类的析构函数+智能指针shread_ptr 
//使用一个类对定时任务进行封装，类实例化的每一个对象，就是一个定时任务对象，当对象被销毁时自动调用析构，此时就执行这个任务
//shared_ptr 对于空间的管理，因为其底层引用计数的存在，当引用计数为0才会释放指向的资源

using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;
class TimerTask //定时任务
{
private:
    //标识任务的唯一性，一个8byte 的整形当做id , 超时时间 使用32位整形就可以了，所要执行的任务
    uint64_t _id;
    uint32_t _timeout;
    TaskFunc _task_cb;
    //取消任务，利用标志位来标志当前任务的状态；取消任务只要在析构的时候不执行这个任务便可
    bool _canceled;//true 表示任务取消了
    ReleaseFunc _release;

public:
    TimerTask(uint64_t id , uint32_t delay , TaskFunc cb)
    :_id(id),_timeout(delay),_task_cb(cb),_canceled(false)
    {}
    ~TimerTask()
    {
        //析构的时候执行任务，如果任务没有取消的情况下
        if(_canceled==false) _task_cb();
        //删除在事件轮 _timers 的这个任务
        _release();
    }

    void SetRelease(const ReleaseFunc & cb)
    {
        _release = cb;
    }
    //GET方法获取延迟时间
    uint32_t DelayTime()
    {
        return _timeout;
    }
    void SetCancel()
    {
        _canceled = true;
    }
};

class TimerWheel
{
private:
    //我们此处实现简单的秒时间轮，需要秒针 _tick，一个大小为60 的数组，数组中存放的是指向任务的shared_ptr 对象
    //因为shared_ptr 指向同一块资源并不会共享引用计数器，而weak_ptr 并不会影响shared_ptr底层计数器；通过weak_ptr 来构造shared_ptr ，就可以实现共享同一引用计数器了
    int _tick;//当前秒针，走到哪里释放哪里，相当于执行那个地方的任务
    int _capacity;//标识当前时间轮的最大延迟时间（底层vector 的最大容量）
    //两个指针对象
    using WeakPtr = std::weak_ptr<TimerTask>;
    using PtrTask = std::shared_ptr<TimerTask>;

    //时间轮，其中存放的shread_ptr 对象
    std::vector<std::vector<PtrTask>> _wheel;//使用二位数组实现
    //保存任务号与其指针的对应关系
    std::unordered_map<uint64_t , WeakPtr> _timers;
private:
    void RemoveTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if(it != _timers.end()) _timers.erase(it);
    }
public:
    TimerWheel():_capacity(60),_tick(0),_wheel(_capacity)
    {}
    ~TimerWheel(){}
    //向时间轮当中添加定时任务
    void TimerAdd(uint64_t id , uint32_t delay , const TaskFunc& cb)
    {
        //将任务封装成一个定时任务
        PtrTask pt(new TimerTask(id , delay , cb));
        //设置TimeTask 的Realse 操作,利用bind ，实际上就是在_timers 中查找并删除
        pt->SetRelease(std::bind(&TimerWheel::RemoveTimer ,this , id));
        //将定时任务添加进时间轮以及_timers 当中
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(pt);
        _timers[id] = WeakPtr(pt);
    }

    //延迟定时任务
    void TimerRefresh(uint64_t id)
    {
        //根据任务id 从_timers 找到这个定时任务的指针，实例化出shread_ptr 重新加入时间轮当中
        auto it = _timers.find(id);
        if(it == _timers.end())
        {
            return;
        } 
        //将weak_ptr 转换为 shared_ptr
        PtrTask pt = it->second.lock();
        //放入时间轮当中
        int pos = (_tick + pt->DelayTime()) % _capacity;
        _wheel[pos].push_back(pt);
    }

    //取消定时任务
    void TimerCancel(uint64_t id)
    {
        auto it = _timers.find(id);
        if(it == _timers.end()) return;
        //找到了这个任务，就将其设置为取消状态
        PtrTask pt = it->second.lock();
        if(pt) pt->SetCancel();
    }

    //让时间轮运行起来
    void RunTimerWheel()
    {
        //让秒针一秒往后走，并释放对应下标空间中的数据
        _tick = (_tick + 1) % _capacity;
        _wheel[_tick].clear();
    }
};

class Test
{
public:
    Test(){ std::cout << "构造" << std::endl;}
    ~Test() {std::cout << "析构" << std::endl;}
};

//任务
void DelTest(Test* t)
{
    delete t;
}

int main()
{
    //创建时间轮
    TimerWheel tw;
    Test * t = new Test();

    //将任务添加进时间轮当中
    tw.TimerAdd(1234,4,std::bind(DelTest , t));

    //每个一秒延迟一下任务
    for(int i = 0;i<4;i++)
    {
        sleep(1);
        tw.TimerRefresh(1234);
        //让秒针移动
        tw.RunTimerWheel();
        std::cout << "刷新了任务，还需要4秒后才能执行任务" << std::endl;
    }

    //取消任务
    tw.TimerCancel(1234);

    //让秒针继续移动
    while(1)
    {
        sleep(1);
        std::cout << "+++++++++++++++++" << std::endl;
        tw.RunTimerWheel();
    }

    return 0;
}

